package org.example;

import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.example.sdk.AuthResult;
import org.example.sdk.JitConstant;
import org.example.sdk.JitGatewayUtilBean;
import org.example.sdk.JitResult;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.sessions.AuthenticationSessionModel;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.Map;


public class AuthenticatorSpiDemo implements Authenticator {

    private static final String TPL_CODE = "incloud-login-ukey.ftl";
    private static final Logger logger = Logger.getLogger(AuthenticatorSpiDemo.class.getName());

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        AuthenticatorConfigModel config = context.getAuthenticatorConfig();
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        //KeycloakSession session = context.getSession();
        UserModel user = context.getUser();

        LoginFormsProvider forms = context.form();
        forms.setAttribute("realm", context.getRealm());
        forms.setAttribute("username", user.getUsername());

        if(!baseValidate(forms, config)) {
            Response challenge = forms.createInspurForm(TPL_CODE);
            context.challenge(challenge);
            return;
        }
        try{
            JitResult result = UKeyServiceFactory.get(config.getConfig()).randomOrignal();
            //if(result != null && StringUtils.isEmpty(result.getErrorMsg())){
            //错误信息为空，说明无错误
            if(result != null && result.isSuccess()){
                String original = (String) result.getData();
                //原文放到session中
                authSession.setAuthNote(JitConstant.AuthConstant.KEY_ORIGINAL_DATA, original);
                forms.setAttribute("original", original);
                Response challenge = forms.createInspurForm(TPL_CODE);
                context.challenge(challenge);
            }else {
                String errCode = "";
                String errMsg = "";
                if (result != null) {
                    errCode = result.getErrCode();
                    errMsg = result.getErrorMsg();
                }
                logger.warning("获取原文异常:" + errMsg);
				/*context.failureChallenge(AuthenticationFlowError.ACCESS_DENIED,
						context.form().setError("ukeyAuthError", errCode + " & " + errMsg)
								.createErrorPage(Response.Status.BAD_REQUEST));*/
                forms.setError("[" + errCode + "]" + errMsg);
                Response challenge = forms.createInspurForm(TPL_CODE);
                context.challenge(challenge);
                return;
            }
        } catch (Exception e) {
            logger.severe(e.getMessage());
            context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,
                    context.form().setError("ukeyAuthError", e.getMessage())
                            .createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));
        }
    }

    @Override
    public void action(AuthenticationFlowContext context) {
        AuthenticatorConfigModel config = context.getAuthenticatorConfig();
        AuthenticationSessionModel authSession = context.getAuthenticationSession();
        UserModel user = context.getUser();
        String username = user.getUsername();
        MultivaluedMap<String, String> parameters = context.getHttpRequest().getDecodedFormParameters();
        //认证方式
        String authMode = parameters.getFirst(JitConstant.AuthConstant.MSG_AUTH_MODE);
        //原文
        String original = parameters.getFirst(JitConstant.AuthConstant.KEY_ORIGINAL);
        //签名数据
        String signedData = parameters.getFirst(JitConstant.AuthConstant.KEY_SIGNED_DATA);

        //从session中获取orignal_data
        String originalData = authSession.getAuthNote(JitConstant.AuthConstant.KEY_ORIGINAL_DATA);

        logger.info("action===authMode:" + authMode);
        logger.info("action===original:" + original);
        logger.info("action===signedData:" + signedData);
        logger.info("action===originalData:" + originalData);

        LoginFormsProvider forms = context.form();
        forms.setAttribute("realm", context.getRealm());
        forms.setAttribute("username", username);
        forms.setAttribute("original", originalData);

//        if(!baseValidate(forms, config)) {
//            Response challenge = forms.createInspurForm(TPL_CODE);
//            context.challenge(challenge);
//            return;
//        }
        if (StringUtils.isBlank(original)) {
            forms.setError("认证原文为空.请刷新重新登录");
            Response challenge = forms.createInspurForm(TPL_CODE);
            context.challenge(challenge);
            return;
        }
        JitGatewayUtilBean bean = new JitGatewayUtilBean();
        bean.setAuthMode(authMode);
        bean.setOriginal_jsp(original);
        bean.setSigned_data(signedData);
        bean.setToken(parameters.getFirst(JitConstant.AuthConstant.MSG_TOKEN));
        bean.setOriginal_data(originalData);
        bean.setRemoteAddr(getClientIP(context));//客户端IP
        // 从cookie中取得二维码随机数
        // 暂时应该用不到二维码相关的

        // 调用网关工具类方式进行身份认证
        try {
            AuthResult result = UKeyServiceFactory.get(config.getConfig()).authGateway(bean);
            if(result.isSuccess()) {
                //校验 登录用户名中和ukey中是否一致
                String userAccount = null;
                Map certMap = result.getCertAttributeNodeMap();
                String info = null;
                for (Object o : certMap.entrySet()) {
                    Map.Entry<String[],String> entry = (Map.Entry<String[], String>) o;
                    String[] tmpkeyes = entry.getKey();
                    if("X509Certificate.SubjectDN".equalsIgnoreCase(tmpkeyes[0])){
                        info = entry.getValue();
                        break;
                    }
                }
                if (info != null) {
                    String[] infoArr = info.split(",");
                    for (String s : infoArr) {
                        String[] temp = s.split("=");
                        if (temp.length > 1) {
                            if("CN".equalsIgnoreCase(temp[0])){
                                userAccount = temp[1].trim();
                                break;
                            }
                        }
                    }
                }
                if (username.equalsIgnoreCase(userAccount)) {
                    //名称一致，校验通过
                    context.success();
                } else {
                    //名称不一致
                    logger.warning("登录用户和证书用户不一致");
					/*context.failureChallenge(AuthenticationFlowError.ACCESS_DENIED,
							context.form().setError("ukeyAuthError", "登录用户和证书用户不一致")
									.createErrorPage(Response.Status.BAD_REQUEST));*/
                    forms.setError("登录用户和证书用户不一致");
                    Response challenge = forms.createInspurForm(TPL_CODE);
                    context.challenge(challenge);
                    return;
                }
                //校验是否过期
                //经测试发现证书过期后，吉大正元给的demo界面上看不到过期的证书，因此可以暂时不做过期校验
            } else {
                //ukey登录失败
                logger.warning("吉大正元网关校验失败." + result.getErrDesc());
				/*
				context.failureChallenge(AuthenticationFlowError.ACCESS_DENIED,
						context.form().setError("ukeyAuthError", result.getErrDesc())
								.createErrorPage(Response.Status.BAD_REQUEST));
				 */
                forms.setError(result.getErrDesc());
                Response challenge = forms.createInspurForm(TPL_CODE);
                context.challenge(challenge);
                return;
            }
        } catch (Exception e) {
            logger.warning(e.getMessage());
            context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,
                    context.form().setError("ukeyAuthError", e.getMessage())
                            .createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));
        }
    }

    @Override
    public boolean requiresUser() {
        return true;
    }

    @Override
    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
        return true;
    }

    @Override
    public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
    }

    @Override
    public void close() {
    }
    private String getClientIP(AuthenticationFlowContext context) {
        String ip = context.getEvent().getEvent().getIpAddress();
        return !ip.isEmpty() ? ip : "";
    }

    /**
     * 基础校验. true校验通过
     * @param forms
     * @param config
     * @return
     */
    private boolean baseValidate(LoginFormsProvider forms, AuthenticatorConfigModel config) {
        if(null == config || null == config.getConfig()){
            logger.warning("获取config为空.异常.");
            forms.setError("ukey认证流config配置为空");
            return false; //校验没通过
        }
        String authURL = config.getConfig().get("authURL");
        String appId = config.getConfig().get("appId");
        logger.info("jit auth url:" + authURL + ", appId:" + appId);
        if (StringUtils.isBlank(authURL) || authURL.startsWith("http://x")
                || authURL.startsWith("http://X")) { //http://x.x.x.x是默认的初始值
            forms.setError("请先登录administrator console初始化吉大正元网关信息.完成后刷新重登录");
            return false;
        }
        return true;
    }

}
